#pragma once

#include "option.h"

namespace sfc::result {

using option::Option;

template <class T, class E>
struct Result : protected variant::Variant<T, E> {
  using Base = variant::Variant<T, E>;
  using Base::_inn;
  using Base::_tag;

  [[sfc_inline]] Result(T t) noexcept : Base{static_cast<T &&>(t)} {}
  [[sfc_inline]] Result(E e) noexcept : Base{static_cast<E &&>(e)} {}

  [[sfc_inline]] Result(const Result &) = default;
  [[sfc_inline]] Result(Result &&) noexcept = default;

  [[sfc_inline]] explicit operator bool() const {
    return _tag == 0;
  }

  [[sfc_inline]] auto is_ok() const -> bool {
    return _tag == 0;
  }

  [[sfc_inline]] auto is_err() const -> bool {
    return _tag == 1;
  }

  [[sfc_inline]] auto ok() && -> Option<T> {
    if (!this->is_ok()) {
      return {};
    }
    return static_cast<T &&>(_inn._0);
  }

  [[sfc_inline]] auto ok() const & -> Option<T> {
    if (!this->is_ok()) {
      return {};
    }
    return _inn._0;
  }

  [[sfc_inline]] auto err() && -> Option<E> {
    if (!this->is_err()) {
      return {};
    }
    return static_cast<E &&>(_inn._1);
  }

  [[sfc_inline]] auto err() const & -> Option<E> {
    if (!this->is_err()) {
      return {};
    }
    return _inn._1;
  }

  // or[move]
  [[sfc_inline]] auto operator||(T def_val) && -> T {
    if (this->is_err()) {
      return def_val;
    }
    return static_cast<T &&>(_inn._0);
  }

  // or[copy]
  [[sfc_inline]] auto operator||(T def_val) const & -> T {
    if (this->is_err()) {
      return def_val;
    }
    return _inn._0;
  }

  // and[move]
  template <class U>
  [[sfc_inline]] auto operator&&(Result<U, E> res) && -> Result<U, E> {
    if (this->is_ok()) {
      return res;
    }
    return static_cast<E &&>(_inn._1);
  }
};

}  // namespace sfc::result
